package cn.sherer.utils;

import java.io.File;
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedHashMap;
import cn.sherer.beans.kuaishou.*;
import java.util.stream.Collectors;
import com.alibaba.fastjson2.JSONObject;
import org.apache.commons.codec.digest.DigestUtils;

import static cn.sherer.utils.WebUtils.*;
import static java.text.MessageFormat.format;

/**
 * 快手工具类
 */
public class KuaishouUtils {

    // <editor-fold defaultstate="collapsed" desc="接口地址">

    /** 接口调用凭据地址（网站、APP、小程序） */
    public static final String TOKEN_URL = "https://open.kuaishou.com/oauth2/access_token";

    /** 登录地址（网站、APP） */
    public static final String LOGIN_URL = "https://open.kuaishou.com/oauth2/access_token?app_id={0}&app_secret={1}&code={2}&grant_type=authorization_code";
    /** 登录地址（小程序） */
    public static final String MP_LOGIN_URL = "https://open.kuaishou.com/oauth2/mp/code2session";

    /** 刷新令牌地址（网站、APP） */
    public static final String REFRESH_TOKEN_URL = "https://open.kuaishou.com/oauth2/refresh_token?app_id={0}&app_secret={1}&refresh_token={2}&grant_type=refresh_token";

    /** 用户信息地址（网站、APP） */
    public static final String USERINFO_URL = "https://open.kuaishou.com/openapi/user_info?app_id={0}&access_token={1}";

    /** 用户手机号地址（网站、APP） */
    public static final String USERPHONE_URL = "https://open.kuaishou.com/openapi/user_phone";

    /** 预下单接口地址（有收银台） */
    public static final String CREATE_ORDER_URL = "https://open.kuaishou.com/openapi/mp/developer/epay/create_order?app_id={0}&access_token={1}";

    /** 预下单接口地址（苹果IAP支付） */
    public static final String CREATE_IAP_ORDER_URL = "https://open.kuaishou.com/openapi/mp/developer/epay/iap/create_order?app_id={0}&access_token={1}";

    /** 订单查询接口地址（有收银台） */
    public static final String QUERY_ORDER_URL = "https://open.kuaishou.com/openapi/mp/developer/epay/query_order?app_id={0}&access_token={1}";

    /** 订单退款接口地址（有收银台） */
    public static final String APPLY_REFUND_URL = "https://open.kuaishou.com/openapi/mp/developer/epay/apply_refund?app_id={0}&access_token={1}";

    /** 退款查询接口地址（有收银台） */
    public static final String QUERY_REFUND_URL = "https://open.kuaishou.com/openapi/mp/developer/epay/query_refund?app_id={0}&access_token={1}";

    /** 结算接口地址（有收银台） */
    public static final String SETTLE_URL = "https://open.kuaishou.com/openapi/mp/developer/epay/settle?app_id={0}&access_token={1}";

    /** 结算查询接口地址（有收银台） */
    public static final String QUERY_SETTLE_URL = "https://open.kuaishou.com/openapi/mp/developer/epay/query_settle?app_id={0}&access_token={1}";

    /** 预下单接口地址（无收银台） */
    public static final String CREATE_ORDER_WITH_CHANNEL_URL = "https://open.kuaishou.com/openapi/mp/developer/epay/create_order_with_channel?app_id={0}&access_token={1}";

    /** 取消支付方式接口地址（无收银台） */
    public static final String CANCEL_CHANNEL_URL = "https://open.kuaishou.com/openapi/mp/developer/epay/cancel_channel?app_id={0}&access_token={1}";

    /** 订单同步接口地址 */
    public static final String REPORT_URL = "https://open.kuaishou.com/openapi/mp/developer/order/v1/report?app_id={0}&access_token={1}";

    /** 通过文件进行图片上传接口地址 */
    public static final String UPLOAD_WITH_FILE_URL = "https://open.kuaishou.com/openapi/mp/developer/file//img/uploadWithFile?app_id={0}&access_token={1}";

    /** 通过url进行图片上传接口地址 */
    public static final String UPLOAD_WITH_URL_URL = "https://open.kuaishou.com/openapi/mp/developer/file//img/uploadWithUrl?app_id={0}&access_token={1}&url={2}";

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="枚举类型">

    /** 应用类型 */
    public enum AppType {
        /** 小程序 */
        MP,
        /** 应用内 */
        WEB,
        /** 网站 */
        H5,
        /** APP */
        APP,
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="AccessToken">

    /**
     * 获取接口调用凭据
     * @param appid 应用ID
     * @param secret 应用密钥
     * @return 接口调用凭据
     */
    public static KsToken getAccessToken(String appid, String secret) {
        String result = post(TOKEN_URL, format("app_id={0}&app_secret={1}&grant_type=client_credentials", appid, secret), KUAISHOU_AGENT);
        System.out.println("快手凭据结果：" + result);
        return JSONObject.parseObject(result, KsToken.class);
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="登录">

    /**
     * 获取登录凭证（网站、APP）
     * @param appid 应用ID
     * @param secret 应用密钥
     * @param code 登录时获取的 code
     * @return 登录凭证
     */
    public static KsLogin getLogin(String appid, String secret, String code) {
        return getLogin(AppType.APP, appid, secret, code);
    }

    /**
     * 获取小程序登录凭证
     * @param appid 应用ID
     * @param secret 应用密钥
     * @param code 登录时获取的 code
     * @return 登录凭证
     */
    public static KsLogin getMPLogin(String appid, String secret, String code) {
        return getLogin(AppType.MP, appid, secret, code);
    }

    /**
     * 获取登录凭证
     * @param type 应用类型
     * @param appid 应用ID
     * @param secret 应用密钥
     * @param code 登录时获取的 code
     * @return 登录凭证
     */
    public static KsLogin getLogin(AppType type, String appid, String secret, String code) {
        String result = type == AppType.MP ? post(MP_LOGIN_URL, format("app_id={0}&app_secret={1}&js_code={2}", appid, secret, code), KUAISHOU_AGENT) : get(format(LOGIN_URL, appid, secret, code), KUAISHOU_AGENT);
        System.out.println("快手登录结果：" + result);
        return JSONObject.parseObject(result, KsLogin.class);
    }

    /**
     * 刷新登录凭证（网站、APP）
     * @param appid 应用ID
     * @param secret 应用密钥
     * @param refreshToken 刷新令牌
     * @return 登录凭证
     */
    public static KsLogin refreshLogin(String appid, String secret, String refreshToken) {
        String result = get(format(REFRESH_TOKEN_URL, appid, secret, refreshToken), KUAISHOU_AGENT);
        return JSONObject.parseObject(result, KsLogin.class);
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="用户信息">

    /**
     * 获取用户信息（公众号、网站、APP）
     * @param appid 应用ID
     * @param token 接口访问凭证
     * @return 用户信息
     */
    public static KsUser getUserInfo(String appid, String token) {
        String result = get(format(USERINFO_URL, appid, token), KUAISHOU_AGENT);
        return JSONObject.parseObject(result, KsUser.class);
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="订阅消息">

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="支付">

    /**
     * 预下单（有收银台）
     * @param appid 应用ID
     * @param secret 应用密钥
     * @param token 接口访问凭证
     * @param data 预下单数据
     * @return 预下单结果
     */
    public static KsOrder createOrder(String appid, String secret, String token, Map<String, Object> data) {
        return createOrder(appid, secret, token, data, false);
    }

    /**
     * 预下单（有收银台）
     * @param appid 应用ID
     * @param secret 应用密钥
     * @param token 接口访问凭证
     * @param data 预下单数据
     * @param isIAP 是否为苹果支付
     * @return 预下单结果
     */
    public static KsOrder createOrder(String appid, String secret, String token, Map<String, Object> data, boolean isIAP) {
        data.put("app_id", appid);
        data.put("sign", getSign(data, secret));
        data.remove("app_id");
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        String result = post(format((isIAP ? CREATE_IAP_ORDER_URL : CREATE_ORDER_URL), appid, token), JSONObject.toJSONString(data), KUAISHOU_AGENT, headers);
        System.out.println("快手有收银台预下单结果：" + result);
        return JSONObject.parseObject(result, KsOrder.class);
    }

    /**
     * 查询订单（有收银台）
     * @param appid 应用ID
     * @param secret 应用密钥
     * @param token 接口访问凭证
     * @param out_order_no 商户订单号
     * @return 查询订单结果
     */
    public static KsPayment queryOrder(String appid, String secret, String token, String out_order_no) {
        Map<String, Object> data = new HashMap<>();
        data.put("app_id", appid);
        data.put("out_order_no", out_order_no);
        data.put("sign", getSign(data, secret));
        data.remove("app_id");
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        String result = post(format(QUERY_ORDER_URL, appid, token), JSONObject.toJSONString(data), KUAISHOU_AGENT, headers);
        System.out.println("快手有收银台查询订单结果：" + result);
        return JSONObject.parseObject(result, KsPayment.class);
    }

    /**
     * 支付退款（有收银台）
     * @param appid 应用ID
     * @param secret 应用密钥
     * @param token 接口访问凭证
     * @param data 退款数据
     * @return 退款结果
     */
    public static KsRefund applyRefund(String appid, String secret, String token, Map<String, Object> data) {
        data.put("app_id", appid);
        data.put("sign", getSign(data, secret));
        data.remove("app_id");
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        String result = post(format(APPLY_REFUND_URL, appid, token), JSONObject.toJSONString(data), KUAISHOU_AGENT, headers);
        System.out.println("快手有收银台退款结果：" + result);
        return JSONObject.parseObject(result, KsRefund.class);
    }

    /**
     * 退款查询（有收银台）
     * @param appid 应用ID
     * @param secret 应用密钥
     * @param token 接口访问凭证
     * @param out_refund_no 商户退款单号
     * @return 退款查询结果
     */
    public static KsRefund queryRefund(String appid, String secret, String token, String out_refund_no) {
        Map<String, Object> data = new HashMap<>();
        data.put("app_id", appid);
        data.put("out_refund_no", out_refund_no);
        data.put("sign", getSign(data, secret));
        data.remove("app_id");
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        String result = post(format(QUERY_REFUND_URL, appid, token), JSONObject.toJSONString(data), KUAISHOU_AGENT, headers);
        System.out.println("快手有收银台查询退款结果：" + result);
        return JSONObject.parseObject(result, KsRefund.class);
    }

    /**
     * 支付结算（有收银台）
     * @param appid 应用ID
     * @param secret 应用密钥
     * @param token 接口访问凭证
     * @param data 结算数据
     * @return 结算结果
     */
    public static KsSettle settle(String appid, String secret, String token, Map<String, Object> data) {
        data.put("app_id", appid);
        data.put("sign", getSign(data, secret));
        data.remove("app_id");
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        String result = post(format(SETTLE_URL, appid, token), JSONObject.toJSONString(data), KUAISHOU_AGENT, headers);
        System.out.println("快手有收银台支付结算结果：" + result);
        return JSONObject.parseObject(result, KsSettle.class);
    }

    /**
     * 结算查询（有收银台）
     * @param appid 应用ID
     * @param secret 应用密钥
     * @param token 接口访问凭证
     * @param out_settle_no 商户结算单号
     * @return 结算查询结果
     */
    public static KsSettle querySettle(String appid, String secret, String token, String out_settle_no) {
        Map<String, Object> data = new HashMap<>();
        data.put("app_id", appid);
        data.put("out_settle_no", out_settle_no);
        data.put("sign", getSign(data, secret));
        data.remove("app_id");
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        String result = post(format(QUERY_SETTLE_URL, appid, token), JSONObject.toJSONString(data), KUAISHOU_AGENT, headers);
        System.out.println("快手有收银台查询结算结果：" + result);
        return JSONObject.parseObject(result, KsSettle.class);
    }

    /**
     * 预下单（无收银台）
     * @param appid 应用ID
     * @param secret 应用密钥
     * @param token 接口访问凭证
     * @param data 预下单数据
     * @return 预下单结果
     */
    public static KsOrder createOrderWithChannel(String appid, String secret, String token, Map<String, Object> data) {
        data.put("app_id", appid);
        data.put("sign", getSign(data, secret));
        data.remove("app_id");
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        String result = post(format(CREATE_ORDER_WITH_CHANNEL_URL, appid, token), JSONObject.toJSONString(data), KUAISHOU_AGENT, headers);
        System.out.println("快手无收银台预下单结果：" + result);
        return JSONObject.parseObject(result, KsOrder.class);
    }

    /**
     * 取消支付方式（无收银台）
     * @param appid 应用ID
     * @param secret 应用密钥
     * @param token 接口访问凭证
     * @param out_order_no 商户订单号
     * @return 取消支付方式结果
     */
    public static KsOrder cancelChannel(String appid, String secret, String token, String out_order_no) {
        Map<String, Object> data = new HashMap<>();
        data.put("app_id", appid);
        data.put("out_order_no", out_order_no);
        data.put("sign", getSign(data, secret));
        data.remove("app_id");
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        String result = post(format(CANCEL_CHANNEL_URL, appid, token), JSONObject.toJSONString(data), KUAISHOU_AGENT, headers);
        System.out.println("快手无收银台取消支付方式结果：" + result);
        return JSONObject.parseObject(result, KsOrder.class);
    }

    /**
     * 订单同步
     * @param appid 应用ID
     * @param token 接口访问凭证
     * @param data 订单数据
     * @return 订单同步结果
     */
    public static KsResult report(String appid, String token, Map<String, Object> data) {
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        String result = post(format(REPORT_URL, appid, token), JSONObject.toJSONString(data), KUAISHOU_AGENT, headers);
        System.out.println("快手订单同步结果：" + result);
        return JSONObject.parseObject(result, KsResult.class);
    }

    /**
     * 获取参数 Map 的签名结果
     * @param signParamsMap 参与签名的参数 Map
     * @param secret 应用密钥
     * @return 返回签名结果
     */
    public static String getSign(Map<String, Object> signParamsMap, String secret) {
        // 去掉 value 为空的
        Map<String, Object> trimmedParamMap = signParamsMap.entrySet().stream().filter(item -> item.getValue() != null && !item.getValue().toString().isEmpty()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        // 按照字母排序
        Map<String, Object> sortedParamMap = trimmedParamMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new));

        // 组装成待签名字符串。(注，引用了guava工具)
        String paramStr = sortedParamMap.entrySet().stream().map(item -> item.getKey() + "=" + item.getValue()).collect(Collectors.joining("&"));
        String signStr = paramStr + secret;

        // 生成签名返回。(注，引用了commons-codec工具)
        return DigestUtils.md5Hex(signStr);
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="图片上传">

    /**
     * 通过文件进行图片上传
     * @param appid 应用ID
     * @param token 接口访问凭证
     * @param filePath 图片路径
     * @return 图片信息
     */
    public static KsImgInfo uploadWithFile(String appid, String token, String filePath) {
        return uploadWithFile(appid, token, new File(filePath));
    }

    /**
     * 通过文件进行图片上传
     * @param appid 应用ID
     * @param token 接口访问凭证
     * @param file 图片文件
     * @return 图片信息
     */
    public static KsImgInfo uploadWithFile(String appid, String token, File file) {
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "multipart/form-data");
        String result = request(format(UPLOAD_WITH_FILE_URL, appid, token), file, KUAISHOU_AGENT, headers);
        System.out.println("快手通过文件进行图片上传结果：" + result);
        return JSONObject.parseObject(result, KsImgInfo.class);
    }

    /**
     * 通过url进行图片上传
     * @param appid 应用ID
     * @param token 接口访问凭证
     * @param url 图片地址
     * @return 图片信息
     */
    public static KsImgInfo uploadWithUrl(String appid, String token, String url) {
        String result = post(format(UPLOAD_WITH_URL_URL, appid, token, url), KUAISHOU_AGENT);
        System.out.println("快手通过url进行图片上传结果：" + result);
        return JSONObject.parseObject(result, KsImgInfo.class);
    }

    // </editor-fold>

}
